#ifndef PM_H
#define PM_H

#ifdef __cplusplus
extern "C" {
#endif


#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <arpa/inet.h>
#include <sys/epoll.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/time.h>
#include <mtcp_api.h>
#include <mtcp_epoll.h>
#include "cpu.h"
#include "rss.h"
#include "http_parsing.h"
#include "debug.h"
#include "../definition.h"
#include<linux/types.h>

#define PM_FAKEFDS 5000 // number of fake fds
#define MAX_CPUS 16
#define LATENCY_LINE 400


#define SERV_IP "10.10.1.2"
#define SERV_PORT 11211


#define ENABLE_DBG (1)

#define MAX_URL_LEN 128
#define MAX_FILE_LEN 128
#define HTTP_HEADER_LEN 1024
#define MAX_DT_LEN 2000
#define US_PER_SECOND 1000*1000
#define IP_RANGE 1
#define MAX_IP_STR_LEN 16

#define MAX_KEY_LEN (1024)
#define MAX_VAL_LEN (1024)
#define cpu_MHZ 2400
#define MIN_LATENCY 100
#define MAX_LATENCY 800
#define SLOT_INTERVAL 10
#define SLOT_NUM 34
#define LATENCY_PERCENT 0.99
#define BUF_SIZE (8*1024)

#define CALC_MD5SUM FALSE

#define TIMEVAL_TO_MSEC(t)		((t.tv_sec * 1000) + (t.tv_usec / 1000))
#define TIMEVAL_TO_USEC(t)		((t.tv_sec * 1000000) + (t.tv_usec))
#define TS_GT(a,b)				((int64_t)((a)-(b)) > 0)

#define MAX(a, b) ((a)>(b)?(a):(b))
#define MIN(a, b) ((a)<(b)?(a):(b))

#ifndef TRUE
#define TRUE (1)
#endif

#ifndef FALSE
#define FALSE (0)
#endif

#ifndef ERROR
#define ERROR (-1)
#endif

/*----------------------------------------------------------------------------*/
static pthread_t app_thread[MAX_CPUS];
static pthread_t pth[MAX_CPUS];
static mctx_t g_mctx[MAX_CPUS];
static int done[MAX_CPUS];

/*----------------------------------------------------------------------------*/
static int num_cores;
static unsigned long thread_count[MAX_CPUS];
static unsigned long prv_count[MAX_CPUS];
struct timeval total_cur_time[MAX_CPUS];
struct timeval total_pre_time[MAX_CPUS];

static int time_bin[] = {0, 20, 40, 60, 80, 100, 120,  140, 160, 180, 200, 220, 240, 260, 280, 300, 320, 340, 360, 380, 400, 420, 440, 440, 460, 480, 500, 525, 550, 575, 600, 650, 700,800 };

static unsigned long time_slot [MAX_CPUS][SLOT_NUM];
static int num_resp[MAX_CPUS];
static int core_limit;
/*----------------------------------------------------------------------------*/
static char host[MAX_IP_STR_LEN + 1];
static char url[MAX_URL_LEN + 1];
static in_addr_t daddr;
static in_port_t dport;
static in_addr_t saddr;
/*----------------------------------------------------------------------------*/
static int dt_len = 0;
static int num_iter = 0;
static int concurrency = 5;
static int sum_iter = 1;
static int trans_iter[1000];
static int max_fds;
static unsigned long seed = 0;

struct timeval time_start, time_current;
double packet_process_speed;
static double packet_process_latency;
time_t time_interval;
static uint32_t print_size = 200;


typedef struct k_v_pair_st {
    char key[MAX_KEY_LEN];
    char value[MAX_VAL_LEN];
    //size_t key_len;
    //size_t value_len;
} k_v_pair;

k_v_pair **k_v_pair_list;

const char key_text[] = "123456789";
const char value_text[] = "abcdefghijklmnopqrstuvwxyz";
static int num_keys;
static int k_length;
static int v_length;
static int gen_fix_keys;
static int set_opt = 0;
int header_op = 0;
typedef struct ev_timer_st {
    struct event *ev;
    struct timeval to;
    int timer_count;
} ev_timer;
ev_timer timer;

/*----------------------------------------------------------------------------*/
static struct thread_context *g_ctx[MAX_CPUS];
static struct wget_stat *g_stat[MAX_CPUS];

struct wget_vars
{
    struct timeval pretime;
    int headerset;
    uint32_t header_len;
    uint64_t file_len;
    uint64_t recv;
    uint64_t write;
    uint32_t write_times;
    uint32_t read_times;

    int resend;
    int use;
    int active;
    int len_out;
    int len_send;
    int tmpindex;
    char kv_buf[MAX_DT_LEN];
    int fd;
    int tmpfd;
    struct timeval pp_time;
    struct timeval pc_time;
};


/*----------------------------------------------------------------------------*/
struct wget_stat
{
    uint64_t waits;
    uint64_t events;
    uint64_t connects;
    uint64_t reads;
    uint64_t writes;
    uint64_t completes;
    uint64_t use;
    uint64_t errors;
    uint64_t timedout;
    struct timeval pretime;
    uint64_t sum_resp_time;
    uint64_t max_resp_time;


    uint64_t num_get_req;
    uint64_t num_get_resp_success;
    uint64_t num_get_resp_fail;
    uint8_t cmd_mode;

    uint64_t num_last_finished_get;
    pthread_mutex_t _lock;


};
/*----------------------------------------------------------------------------*/
struct thread_context
{
    int core;

    mctx_t mctx;
    int ep;
    struct wget_vars *wvars;

    int started;
    int errors;
    int incompletes;
    int done;
    int pending;
    int biggest_fd ; // the biggest fake fd, to
    int realfd_to_fakefd[65536]; // find fake fd by real fd
    struct wget_stat stat;
};
typedef struct thread_context* thread_context_t;
static double per_core_latency[MAX_CPUS];
/*----------------------------------------------------------------------------*/

int find_fake_fd(thread_context_t ctx, int sockfd);
int reflesh_fake_fd(thread_context_t ctx, int fakefd);
inline int pm_createConnection(thread_context_t ctx, const struct sockaddr *addr, socklen_t addrlen);
inline void pm_closeConnection(thread_context_t ctx, int sockid);
int pm_epoll_ctl(thread_context_t ctx, int epid, int op, int sockid, struct mtcp_epoll_event *event);
int pm_epoll_wait(thread_context_t ctx, int epid, struct mtcp_epoll_event *events, int maxevents, int timeout);
ssize_t pm_write(thread_context_t ctx, int sockid, char *buf, size_t len);
ssize_t pm_read(thread_context_t ctx, int sockid, char *buf, size_t len);
int pm_get_real_fd(thread_context_t ctx, int fd);
inline void
CloseConnection(thread_context_t ctx, int sockid);

// internal functions


#ifdef __cplusplus
}
#endif

#endif // PM_H
